/*
 * Copyright 2007-2009 Perttu "celeron55" Ahola <celeron55@gmail.com>
 *
 * This file is part of jukumagic.
 *
 * jukumagic is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jukumagic is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jukumagic.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "jukuapp.h"
#include "common.h"
#include "ratsnest.h"
#include "component.h"
#include "board.h"
#include "netlist.h"
#include "pin.h"
#include <assert.h>
#include <algorithm>

RatsNest::RatsNest()
{
	m_is_inverse = false;
}

RatsNest::~RatsNest()
{
}

void RatsNest::Draw(wxDC &dc, wxWindow *window, wxPoint origin)
{
	/*if(!m_is_inverse) dc.SetPen(wxPen(wxColour(0,150,0), 5));
	else              dc.SetPen(wxPen(wxColour(150,0,0), 5));*/
	//dc.SetBrush(wxBrush(wxColour(250,200,0)));
	if(!m_is_inverse) dc.SetPen(wxPen(wxGetApp().m_settings.m_colour_ratsnest, 5));
	else              dc.SetPen(wxPen(wxGetApp().m_settings.m_colour_ratsnest_inv, 5));
	dc.SetBrush(wxBrush(wxGetApp().m_settings.m_colour_ratsnest_dot));
	for(std::list<RatLine>::iterator i = m_lines.begin(); i != m_lines.end(); i++){
		dc.DrawLine((*i).m_x1+origin.x, (*i).m_y1+origin.y,
				(*i).m_x2+origin.x, (*i).m_y2+origin.y);
		dc.DrawCircle((*i).m_x1+origin.x, (*i).m_y1+origin.y, 20);
		dc.DrawCircle((*i).m_x2+origin.x, (*i).m_y2+origin.y, 20);
	}
}

/*void AddLinesRecur(std::list<Pin*> &net_pins, std::list<RatLine> &lines,
		std::list<Pin*> &added_pins)
{
	if(find(
	for(std::list<Pin*>::iterator i = net_pins.begin();
			i != net_pins.end(); i++){
		
	}
}*/

void RatsNest::Create(Board *board, Netlist *netlist, bool only_selected)
{
	//std::cout<<"RatsNest::Create()"<<std::endl;
	m_lines.clear();
	m_is_inverse = false;
	assert(board && netlist);
	if(!board || !netlist) return;
	//std::cout<<"netlist->m_nets.size()="<<netlist->m_nets.size()<<std::endl;
	for(std::vector<Net*>::iterator i = netlist->m_nets.begin();
			i != netlist->m_nets.end(); i++){
		Net *net = (*i);
		//std::cout<<"checking net name="<<net->m_name.fn_str()<<std::endl;
		//std::list<BoardPoint> net_other_points;
		std::list<Pin*> net_pins;
		Pin* selected_components_pin = NULL;
		//bool ignore = false;
		for(std::list<NetPin>::iterator i=net->m_pins.begin(); i!=net->m_pins.end();i++){
			/*std::cout<<"NetPin: refdes="<<(*i).m_refdes.fn_str()
					<<", num="<<(*i).m_num.fn_str() <<std::endl;*/
			Pin *pin = board->GetPinByNetPin(*i);
			if(!pin) continue;
			Component *c = pin->GetComponent();
			assert(c);

			if(only_selected && c->GetSelected()){
				selected_components_pin = pin;
				continue;
			}

			net_pins.push_back(pin);
		}
		if(only_selected){
			if(selected_components_pin){
				BoardPoint component_pin_point = selected_components_pin->GetComponentPinPosition();
				bool is_connected = false;
				for(std::list<Pin*>::iterator i = net_pins.begin();
						i != net_pins.end(); i++){
					BoardPoint p = (*i)->GetComponentPinPosition();
					if(board->IsConnected(component_pin_point, p)){
						is_connected = true;
						break;
					}
				}
				if(is_connected) continue;
				for(std::list<Pin*>::iterator i = net_pins.begin();
						i != net_pins.end(); i++){
					BoardPoint p = (*i)->GetComponentPinPosition();
					RatLine l;
					l.m_x1 = component_pin_point.m_x;
					l.m_y1 = component_pin_point.m_y;
					l.m_x2 = p.m_x;
					l.m_y2 = p.m_y;
					m_lines.push_back(l);
				}
			}
		}
		else{
			std::list<Pin*> added_pins;
			for(std::list<Pin*>::iterator i = net_pins.begin();
					i != net_pins.end(); i++){
				Pin *pin1 = (*i);
				added_pins.push_back(pin1);
				BoardPoint pin1p = pin1->GetComponentPinPosition();
				int mindistance = BIG_INT_VALUE;
				Pin *nearest_pin = NULL;
				//find nearest pin that has been not yet added
				for(std::list<Pin*>::iterator i = net_pins.begin();
						i != net_pins.end(); i++){
					Pin *tpin = (*i);
					if(find(added_pins.begin(), added_pins.end(), tpin) != added_pins.end()){
						//already added
						continue;
					}
					BoardPoint tpinp = tpin->GetComponentPinPosition();
					int d = BoardPoint::DistanceSquared(pin1p, tpinp);
					assert(d >= 0);
					if(d < mindistance){
						mindistance = d;
						nearest_pin = tpin;
					}
				}
				if(nearest_pin == NULL) break;
				BoardPoint nearest_pinp = nearest_pin->GetComponentPinPosition();
				if(!board->IsConnected(pin1p, nearest_pinp)){
					RatLine l;
					l.m_x1 = pin1p.m_x;
					l.m_y1 = pin1p.m_y;
					l.m_x2 = nearest_pinp.m_x;
					l.m_y2 = nearest_pinp.m_y;
					m_lines.push_back(l);
				}
				if(added_pins.size() == net_pins.size()) break;
			}
		}
	}
}

void RatsNest::CreateInv(Board *board, Netlist *netlist, bool only_selected)
{
	//std::cout<<"RatsNest::CreateInv()"<<std::endl;
	m_lines.clear();
	m_is_inverse = true;
	assert(board && netlist);
	if(!board || !netlist) return;
	for(std::list<JukuObject*>::const_iterator i = board->GetObjects().begin();
			i != board->GetObjects().end(); i++){
		JukuObject *object = (*i);
		if(object->GetType() != TYPE_COMPONENT) continue;
		if(only_selected && object->GetSelected() == false) continue;
		Component *c = (Component*)object;
		if(only_selected && !c->GetSelected()) continue;
		Footprint *f = c->GetFootprint();
		for(std::list<JukuObject*>::const_iterator i = f->m_pins.GetObjects().begin();
				i != f->m_pins.GetObjects().end(); i++){
			JukuObject *object = (*i);
			if(object->GetType() != TYPE_PIN) continue;
			Pin *pin = (Pin*)object;
			NetPin p(c->GetRefdes(), pin->m_num);

			BoardHPoint p1(BoardPoint(c->GetBoardPos().m_x + pin->m_x, c->GetBoardPos().m_y + pin->m_y));

			/*std::cout<<"("<<p1.m_x<<","<<p1.m_y<<") refdes="
					<<c->GetRefdes().fn_str()<<", pin num="<<pin->m_num<<std::endl;*/
					
			Net *net = netlist->GetNetByPin(p);

			std::set<int> points;
			board->GetConnected(p1, points);
			for(std::set<int>::iterator i = points.begin(); i != points.end(); i++){
				BoardHPoint p = board->IntPointToBoardHPoint(*i);
				//std::cout<<"("<<x<<","<<y<<") is connected"<<std::endl;
				if(p == p1) continue;
				NetPin np;
				if(!board->GetNetPinByPoint(p, &np)){
					//there's no pin at this point
					continue;
				}
				bool valid_connection = true;
				if(net){
					if(!net->HavePin(np)){
						//pin is connected but it shouldn't be - that's what we're searching for
						valid_connection = false;
					}
				}
				else{
					valid_connection = false;
				}
				if(!valid_connection){
					RatLine l;
					l.m_x1 = p1.m_x*100;
					l.m_y1 = p1.m_y*100;
					l.m_x2 = p.m_x*100;
					l.m_y2 = p.m_y*100;
					std::list<RatLine>::iterator fi = find(m_lines.begin(), m_lines.end(), l);
					if(fi == m_lines.end()){
						m_lines.push_back(l);
					}
				}
			}
		}
	}
}

void RatsNest::Clear()
{
	std::cout<<"RatsNest::Clear()"<<std::endl;
	m_lines.clear();
}

